Skip to content

How To: Manually transfer user scripts from Greasemonkey to Scriptish

smk64 edited this page Jan 19, 2013 · 15 revisions

Until a better solution is created, you will have to manually transfer your user scripts from Greasemonkey to Scriptish.

Step One: Move scripts to "scriptish_scripts" folder

  1. Find your profile folder. You should see a folder named gm_scripts, containing all the user scripts installed that you had installed for Greasemonkey.
  2. If you have already installed Scriptish you'll also see a folder named scriptish_scripts. It's created automatically when you install Scriptish, containing two files (scriptish-blocklist.json & scriptish-config.json) a few bytes each. Delete the scriptish-config.json file and then copy the contents of the gm_scripts folder into the scriptish_scripts folder.
  3. Otherwise (if there isn't a scriptish_scripts folder already), create a copy of the gm_scripts folder and call it scriptish_scripts, or, if you no longer wish to use Greasemonkey, simply rename the gm_scripts folder.

In any case, it is strongly recommended that you uninstall Greasemonkey and close Firefox before renaming the folder. Also, it's crucial that you delete the scriptish-config.json (if it exists) in order to be recreated for the added scripts the next time you launch Firefox, otherwise the User Scripts list tab will appear empty.

(Optional) Step Two: Copy/Transfer settings

This step is the really painful part, especially for some scripts, so if you can live with your settings being reset skip these instructions.

  1. Go to about:config and put greasemonkey.scriptvals into the filter bar
  2. Copy each entry's Name, Value, and Type to a text file
  3. For each name, replace greasemonkey.scriptvals with extensions.scriptish.scriptvals
  4. Right-click in the about:config window and create a new String, Integer, or Boolean with the modified Name and Value pair

Also you can open prefs.js with your favorite plain-text editor and do step 3. Namespace separator is @ in scriptish and / in greasemonkey, thats why you need to replace it too.

Here's a python 3 program to automate the conversion, put prefs.js and scriptish-config.json in the same directory as the script

#by smk
import json
import re
from bisect import bisect_left

def anyStartsWith(word_fragment,wordlist):
    '''
    assumes that wordlist is sorted
    '''
    try:
        closest=wordlist[bisect_left(wordlist, word_fragment)-1]
        if word_fragment.startswith(closest):
            return closest
    except IndexError:
        #word_fragment is greater than all entries in wordlist
        return None


class KeyConverter:
    def __init__(self):
        self.gmScriptInfos=self.getGMScriptInfos()
        self.gmAddonId='extensions.greasemonkey.scriptvals'
        self.scriptishAddonId='extensions.scriptish.scriptvals'

    def getGMScriptInfos(self):
        '''
        returns:
            {scriptId: (namespace,name)}
        '''
        scriptInfos={}
        scriptishConfig=json.load(open('scriptish-config.json','r'))
        for script in scriptishConfig['scripts']:
            namespace=script['namespace']
            name=script['name']
            scriptInfos[namespace+'/'+name]=(namespace,name)
        #make sure there are no namespace ambiguity, by checking if one namespace can be the substring of another
        #using pairs as an algorithm is incorrect, consider ['a','ab','ac']
        startsWithList=scriptInfos.keys()
        for scriptId in startsWithList:
            curStartsWithList=set(startsWithList)
            curStartsWithList.remove(scriptId)
            curStartsWithList=sorted(curStartsWithList)
            closest=anyStartsWith(scriptId,curStartsWithList)
            if closest is not None:
                raise Exception('scriptId {!r} starts with scriptId {!r}'.format(scriptId,closest))
        return scriptInfos
    
    def convertKey(self,key):
        fragment=key[len(self.gmAddonId)+1:]
        
        #greasemonkey allows '.', and '/' in GM_setValue, and also allows them in the namespace & script names, so there is no way to no where the namespace ends
        #    without getting the script ids
        startsWithList=sorted(self.gmScriptInfos.keys())
        scriptId=anyStartsWith(fragment,startsWithList)
        if scriptId is None:
            raise ValueError('script with key {!r} does\'nt exist'.format(key))
        scriptKey=fragment[len(scriptId)+1:]
        namespace,name=self.gmScriptInfos[scriptId]
        
        #remove special chars from namespace, see extensions/modules/script/script.js > nonIdChars
        nonIdChars=r'[^\w@\.\-_]+'
        namespace=re.sub(nonIdChars,'',namespace)
        name=re.sub(nonIdChars,'',name)
        scriptId='@'.join((name,namespace))
        
        return '.'.join([self.scriptishAddonId,scriptId,scriptKey])


def main():
    keyConverter=KeyConverter()
    
    sr=open('prefs.js','rb')
    outSr=open('prefs.out.js','wb')
    for line in sr.readlines():
        line=line.decode()
        if not line.startswith('user_pref('):
            outSr.write(line.encode())
        else:
            startIndex=len('user_pref(')
            endIndex=line.rindex(');')
            key,value=json.loads('['+line[startIndex:endIndex]+']')
            if key.startswith(keyConverter.gmAddonId+'.'):
                try:
                    key=keyConverter.convertKey(key)
                except ValueError as e:
                    print(e)
            line=json.dumps([key,value])[1:-1]
            outSr.write(('user_pref('+line+');\r\n').encode())
        
        
if __name__=='__main__':
    main()


Alternative Method: Symlinks

Another way to is to create a symlink to the folder gm_scripts, and call it scriptish_scripts (or visa versa). Using this method, you will store all of your scripts, their resources, and settings in one folder, where they will be shared by Scriptish and Greasemonkey.